/*
author:杨文�?
email:yangwenchao@keynection.cn
*/

#include <stdatomic.h>
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/event_groups.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
#include "sys/lock.h"
#include "driver/uart.h"
#include "soc/dport_access.h"
#include "esp_log.h"
#include "geminai_log.h"
#include "sdkconfig.h"
#include "mbport.h"
#include "mb_master.h"
#include "mbconfig.h"
#include "mbport.h"


static const CHAR *TAG = "GEMINAI_MODBUS";

/*临界区锁*/
static _lock_t s_port_lock;


/*事件*/

// Event bit mask for eMBMasterWaitRequestFinish()
#define MB_EVENT_REQ_MASK   (EventBits_t)( EV_MASTER_PROCESS_SUCCESS | \
                                            EV_MASTER_ERROR_RESPOND_TIMEOUT | \
                                            EV_MASTER_ERROR_RECEIVE_DATA | \
                                            EV_MASTER_ERROR_EXECUTE_FUNCTION )

static SemaphoreHandle_t xResourceMasterHdl;
static EventGroupHandle_t xEventGroupMasterHdl;
static EventGroupHandle_t xEventGroupMasterConfirmHdl;
static QueueHandle_t xQueueMasterHdl;

static uint64_t xTransactionID = 0;

/*消息队列*/
#define MB_SERIAL_RX_SEMA_TOUT_MS   (1000)
#define MB_SERIAL_RX_SEMA_TOUT      (pdMS_TO_TICKS(MB_SERIAL_RX_SEMA_TOUT_MS))
#define MB_SERIAL_RX_FLUSH_RETRY    (2)

// A queue to handle UART event.
static QueueHandle_t xMbUartQueue;
static TaskHandle_t  xMbTaskHandle;

// The UART hardware port number
static UCHAR ucUartNumber = UART_NUM_MAX - 1;

static BOOL bRxStateEnabled = FALSE; // Receiver enabled flag
static BOOL bTxStateEnabled = FALSE; // Transmitter enabled flag

static SemaphoreHandle_t xMasterSemaRxHandle; // Rx blocking semaphore handle

static xTimerContext_t* pxTimerContext = NULL;


/*临界区操作函�?*/
inline void
vMBPortEnterCritical(void)
{
    _lock_acquire(&s_port_lock);
}

inline void
vMBPortExitCritical(void)
{
    _lock_release(&s_port_lock);
}


BOOL xMBPortSerialWaitEvent(QueueHandle_t xMbUartQueue, uart_event_t* pxEvent, ULONG xTimeout)
{
    BOOL xResult = (BaseType_t)xQueueReceive(xMbUartQueue, (void*)pxEvent, (TickType_t) xTimeout);
    GEMINAI_LOGD(TAG, "%s, UART event: %u ", __func__, (unsigned)pxEvent->type);
    return xResult;
}



/*
The function is called from RTU module to get processed data buffer. Sets the
received buffer and its length using parameters.
*/
__attribute__ ((weak))
BOOL xMBMasterPortSerialGetResponse( UCHAR **ppucMBSerialFrame, USHORT * usSerialLength )
{
    GEMINAI_LOGD(MB_PORT_TAG, " %s default", __func__);
    return TRUE;
}

/*
The function is called from RTU module to set processed data buffer
to be sent in transmitter state machine.
*/
__attribute__ ((weak))
BOOL xMBMasterPortSerialSendRequest( UCHAR *pucMBSerialFrame, USHORT usSerialLength )
{
    GEMINAI_LOGD(MB_PORT_TAG, "%s default", __func__);
    return TRUE;
}



/*事件函数*/

BOOL
xMBMasterPortEventInit( void )
{
    xEventGroupMasterHdl = xEventGroupCreate();
    xEventGroupMasterConfirmHdl = xEventGroupCreate();
    MB_PORT_CHECK((xEventGroupMasterHdl != NULL) && (xEventGroupMasterConfirmHdl != NULL),
                    FALSE, "mb stack event group creation error.");
    xQueueMasterHdl = xQueueCreate(MB_EVENT_QUEUE_SIZE, sizeof(xMBMasterEventType));
    MB_PORT_CHECK(xQueueMasterHdl, FALSE, "mb stack event group creation error.");
    vQueueAddToRegistry(xQueueMasterHdl, "MbMasterPortEventQueue");
    xTransactionID = 0;
    return TRUE;
}

BOOL MB_PORT_ISR_ATTR
xMBMasterPortEventPost( eMBMasterEventEnum eEvent)
{
    BaseType_t xStatus, xHigherPriorityTaskWoken = pdFALSE;
    assert(xQueueMasterHdl != NULL);
    xMBMasterEventType xEvent;
    xEvent.xPostTimestamp = esp_timer_get_time();
    
    if (eEvent & EV_MASTER_TRANS_START) {
        atomic_store(&(xTransactionID), xEvent.xPostTimestamp);
    }
    xEvent.eEvent = (eEvent & ~EV_MASTER_TRANS_START);

    if( (BOOL)xPortInIsrContext() == TRUE ) {
        xStatus = xQueueSendFromISR(xQueueMasterHdl, (const void*)&xEvent, &xHigherPriorityTaskWoken);
        if ( xHigherPriorityTaskWoken ) {
            portYIELD_FROM_ISR();
        }
        if (xStatus != pdTRUE) {
            ESP_EARLY_LOGV(MB_PORT_TAG, "%s: Post message failure = %d.", __func__, xStatus);
            return FALSE;
        }
    } else {
        xStatus = xQueueSend(xQueueMasterHdl, (const void*)&xEvent, MB_EVENT_QUEUE_TIMEOUT);
        MB_PORT_CHECK((xStatus == pdTRUE), FALSE, "%s: Post message failure.", __func__);
    }
    return TRUE;
}

BOOL
xMBMasterPortEventGet(xMBMasterEventType *peEvent)
{
    assert(xQueueMasterHdl != NULL);
    BOOL xEventHappened = FALSE;

    if (xQueueReceive(xQueueMasterHdl, peEvent, portMAX_DELAY) == pdTRUE) {
        peEvent->xTransactionId = atomic_load(&xTransactionID);
        // Set event bits in confirmation group (for synchronization with port task)
        xEventGroupSetBits(xEventGroupMasterConfirmHdl, peEvent->eEvent);
        peEvent->xGetTimestamp = esp_timer_get_time();
        xEventHappened = TRUE;
    }
    return xEventHappened;
}

eMBMasterEventEnum
xMBMasterPortFsmWaitConfirmation( eMBMasterEventEnum eEventMask, ULONG ulTimeout)
{
    EventBits_t uxBits;
    uxBits = xEventGroupWaitBits( xEventGroupMasterConfirmHdl,  // The event group being tested.
                                    eEventMask,                 // The bits within the event group to wait for.
                                    pdFALSE,                    // Keep masked bits.
                                    pdFALSE,                    // Don't wait for both bits, either bit will do.
                                    ulTimeout);                 // Wait timeout for either bit to be set.
    if (ulTimeout && uxBits) {
        // Clear confirmation events that where set in the mask
        xEventGroupClearBits( xEventGroupMasterConfirmHdl, (uxBits & eEventMask) );
    }
    return (eMBMasterEventEnum)(uxBits & eEventMask);
}

uint64_t xMBMasterPortGetTransactionId( )
{
    return atomic_load(&xTransactionID);
}

// This function is initialize the OS resource for modbus master.
void vMBMasterOsResInit( void )
{
    xResourceMasterHdl = xSemaphoreCreateBinary();
    MB_PORT_CHECK((xResourceMasterHdl != NULL), ; , "%s: Resource create error.", __func__);
}

/*
This function is take Mobus Master running resource.
Note:The resource is define by Operating System.

@param lTimeOut the waiting time.

@return resource take result
*/
BOOL xMBMasterRunResTake( LONG lTimeOut )
{
    BaseType_t xStatus = pdTRUE;
    xStatus = xSemaphoreTake( xResourceMasterHdl, lTimeOut );
    MB_PORT_CHECK((xStatus == pdTRUE), FALSE , "%s: Resource take failure.", __func__);
    GEMINAI_LOGD(MB_PORT_TAG,"%s:Take MB resource (%lu ticks).", __func__, lTimeOut);
    return TRUE;
}

/*
This function is release Modbus Master running resource.
Note:The resource is define by Operating System. If you not use OS this function can be empty.
*/
void vMBMasterRunResRelease( void )
{
    BaseType_t xStatus = pdFALSE;
    xStatus = xSemaphoreGive( xResourceMasterHdl );
    if (xStatus != pdTRUE) {
        GEMINAI_LOGD(MB_PORT_TAG,"%s: Release resource fail.", __func__);
    }
}

/*
This is modbus master respond timeout error process callback function.
@note There functions will block modbus master poll while execute OS waiting.

@param ucDestAddress destination salve address
@param pucPDUData PDU buffer data
@param ucPDULength PDU buffer length

*/
void vMBMasterErrorCBRespondTimeout(UCHAR ucDestAddress, const UCHAR* pucPDUData, USHORT ucPDULength)
{
    (void)xEventGroupSetBits( xEventGroupMasterHdl, EV_MASTER_ERROR_RESPOND_TIMEOUT );
    GEMINAI_LOGD(MB_PORT_TAG,"%s:Callback respond timeout.", __func__);
}

/*
This is modbus master receive data error process callback function.
@note There functions will block modbus master poll while execute OS waiting.

@param ucDestAddress destination salve address
@param pucPDUData PDU buffer data
@param ucPDULength PDU buffer length
*/
void vMBMasterErrorCBReceiveData(UCHAR ucDestAddress, const UCHAR* pucPDUData, USHORT ucPDULength)
{
    (void)xEventGroupSetBits( xEventGroupMasterHdl, EV_MASTER_ERROR_RECEIVE_DATA );
    GEMINAI_LOGD(MB_PORT_TAG,"%s:Callback receive data timeout failure.", __func__);
    ESP_LOG_BUFFER_HEX_LEVEL("Err rcv buf", (void *)pucPDUData, (USHORT)ucPDULength, ESP_LOG_DEBUG);
}

/*
This is modbus master execute function error process callback function.
@note There functions will block modbus master poll while execute OS waiting.
So,for real-time of system.Do not execute too much waiting process.

@param ucDestAddress destination salve address
@param pucPDUData PDU buffer data
@param ucPDULength PDU buffer length

*/
void vMBMasterErrorCBExecuteFunction(UCHAR ucDestAddress, const UCHAR* pucPDUData, USHORT ucPDULength)
{
    xEventGroupSetBits( xEventGroupMasterHdl, EV_MASTER_ERROR_EXECUTE_FUNCTION );
    GEMINAI_LOGD(MB_PORT_TAG,"%s:Callback execute data handler failure.", __func__);
    ESP_LOG_BUFFER_HEX_LEVEL("Exec func buf", (void*)pucPDUData, (USHORT)ucPDULength, ESP_LOG_DEBUG);
}

/*
This is modbus master request process success callback function.
@note There functions will block modbus master poll while execute OS waiting.
So,for real-time of system. Do not execute too much waiting process.
*/
void vMBMasterCBRequestSuccess( void ) 
{
    (void)xEventGroupSetBits( xEventGroupMasterHdl, EV_MASTER_PROCESS_SUCCESS );
    GEMINAI_LOGD(MB_PORT_TAG,"%s: Callback request success.", __func__);
}

/*
This function is wait for modbus master request finish and return result.
Waiting result include request process success, request respond timeout,
receive data error and execute function error.You can use the above callback function.
@note If you are use OS, you can use OS's event mechanism. Otherwise you have to run
much user custom delay for waiting.

@return request error code
*/
eMBMasterReqErrCode eMBMasterWaitRequestFinish( void ) {
    eMBMasterReqErrCode eErrStatus = MB_MRE_NO_ERR;
    eMBMasterEventEnum xRecvedEvent;

    EventBits_t uxBits = xEventGroupWaitBits( xEventGroupMasterHdl, // The event group being tested.
                                                MB_EVENT_REQ_MASK,  // The bits within the event group to wait for.
                                                pdTRUE,             // Masked bits should be cleared before returning.
                                                pdFALSE,            // Don't wait for both bits, either bit will do.
                                                portMAX_DELAY );    // Wait forever for either bit to be set.
    xRecvedEvent = (eMBMasterEventEnum)(uxBits);
    if (xRecvedEvent) {
        GEMINAI_LOGD(MB_PORT_TAG,"%s: returned event = 0x%x", __func__, (int)xRecvedEvent);
        if (!(xRecvedEvent & MB_EVENT_REQ_MASK)) {
            // if we wait for certain event bits but get from poll subset
            GEMINAI_LOGE(MB_PORT_TAG,"%s: incorrect event set = 0x%x", __func__, (int)xRecvedEvent);
        }
        xEventGroupSetBits( xEventGroupMasterConfirmHdl, (xRecvedEvent & MB_EVENT_REQ_MASK) );
        if (MB_PORT_CHECK_EVENT(xRecvedEvent, EV_MASTER_PROCESS_SUCCESS)) {
            eErrStatus = MB_MRE_NO_ERR;
        } else if (MB_PORT_CHECK_EVENT(xRecvedEvent, EV_MASTER_ERROR_RESPOND_TIMEOUT)) {
            eErrStatus = MB_MRE_TIMEDOUT;
        } else if (MB_PORT_CHECK_EVENT(xRecvedEvent, EV_MASTER_ERROR_RECEIVE_DATA)) {
            eErrStatus = MB_MRE_REV_DATA;
        } else if (MB_PORT_CHECK_EVENT(xRecvedEvent, EV_MASTER_ERROR_EXECUTE_FUNCTION)) {
            eErrStatus = MB_MRE_EXE_FUN;
        }
    } else {
        GEMINAI_LOGE(MB_PORT_TAG,"%s: Incorrect event or timeout xRecvedEvent = 0x%x", __func__, (int)uxBits);
        // https://github.com/espressif/esp-idf/issues/5275
        // if a no event is received, that means vMBMasterPortEventClose()
        // has been closed, so event group has been deleted by FreeRTOS, which
        // triggers the send of 0 value to the event group to unlock this task
        // waiting on it. For this patch, handles it as a time out without assert.
        eErrStatus = MB_MRE_TIMEDOUT;
    }
    return eErrStatus;
}

void vMBMasterPortEventClose(void)
{
    if (xEventGroupMasterHdl) {
        vEventGroupDelete(xEventGroupMasterHdl);
        xEventGroupMasterHdl = NULL;
    }
    if (xQueueMasterHdl) {
        vQueueDelete(xQueueMasterHdl);
        xQueueMasterHdl = NULL;
    }
    if (xEventGroupMasterConfirmHdl) {
        vEventGroupDelete(xEventGroupMasterConfirmHdl);
        xEventGroupMasterConfirmHdl = NULL;
    }
    if (xResourceMasterHdl) {
        vSemaphoreDelete(xResourceMasterHdl);
        xResourceMasterHdl = NULL;
    }
}

void
vMBMasterPortClose( void )
{
    extern void     vMBMasterPortSerialClose( void );
    extern void     vMBMasterPortTimerClose( void );
    extern void     vMBMasterPortEventClose( void );
    vMBMasterPortSerialClose(  );
    vMBMasterPortTimerClose(  );
    vMBMasterPortEventClose(  );
}

/*消息队列函数*/
static BOOL xMBMasterPortRxSemaInit( void )
{
    xMasterSemaRxHandle = xSemaphoreCreateBinary();
    MB_PORT_CHECK((xMasterSemaRxHandle != NULL), FALSE , "%s: RX semaphore create failure.", __func__);
    return TRUE;
}

static void vMBMasterPortRxSemaClose( void )
{
    if (xMasterSemaRxHandle) {
        vSemaphoreDelete(xMasterSemaRxHandle);
        xMasterSemaRxHandle = NULL;
    }
}

static BOOL xMBMasterPortRxSemaTake( LONG lTimeOut )
{
    BaseType_t xStatus = pdTRUE;
    xStatus = xSemaphoreTake(xMasterSemaRxHandle, lTimeOut );
    MB_PORT_CHECK((xStatus == pdTRUE), FALSE , "%s: RX semaphore take failure.", __func__);
    ESP_LOGV(MB_PORT_TAG,"%s:Take RX semaphore (%" PRIu64 " ticks).", __func__, (uint64_t)lTimeOut);
    return TRUE;
}

static void vMBMasterRxSemaRelease( void )
{
    BaseType_t xStatus = pdFALSE;
    xStatus = xSemaphoreGive(xMasterSemaRxHandle);
    if (xStatus != pdTRUE) {
        GEMINAI_LOGD(MB_PORT_TAG,"%s:RX semaphore is free.", __func__);
    }
}

static BOOL vMBMasterRxSemaIsBusy( void )
{
    BaseType_t xStatus = pdFALSE;
    xStatus = (uxSemaphoreGetCount(xMasterSemaRxHandle) == 0) ? TRUE : FALSE;
    return xStatus;
}

void vMBMasterRxFlush( void )
{
    size_t xSize = 1;
    esp_err_t xErr = ESP_OK;
    for (int xCount = 0; (xCount < MB_SERIAL_RX_FLUSH_RETRY) && xSize; xCount++) {
        xErr = uart_get_buffered_data_len(ucUartNumber, &xSize);
        MB_PORT_CHECK((xErr == ESP_OK), ; , "mb flush serial fail, error = 0x%x.", (int)xErr);
        BaseType_t xStatus = xQueueReset(xMbUartQueue);
        if (xStatus) {
            xErr = uart_flush_input(ucUartNumber);
            MB_PORT_CHECK((xErr == ESP_OK), ; , "mb flush serial fail, error = 0x%x.", (int)xErr);
        }
    }
}

void vMBMasterPortSerialEnable(BOOL bRxEnable, BOOL bTxEnable)
{
    // This function can be called from xMBRTUTransmitFSM() of different task
    if (bTxEnable) {
        vMBMasterRxFlush();
        bTxStateEnabled = TRUE;
    } else {
        bTxStateEnabled = FALSE;
    }
    if (bRxEnable) {
        bRxStateEnabled = TRUE;
        vMBMasterRxSemaRelease();
        vTaskResume(xMbTaskHandle); // Resume receiver task
    } else {
        vTaskSuspend(xMbTaskHandle); // Block receiver task
        bRxStateEnabled = FALSE;
    }
}

static USHORT usMBMasterPortSerialRxPoll(size_t xEventSize)
{
    BOOL xStatus = TRUE;
    USHORT usCnt = 0;

    xStatus = xMBMasterPortRxSemaTake(MB_SERIAL_RX_SEMA_TOUT);
    if (xStatus) {
        while(xStatus && (usCnt++ <= xEventSize)) {
            // Call the Modbus stack callback function and let it fill the stack buffers.
            xStatus = pxMBMasterFrameCBByteReceived(); // callback to receive FSM
        }
        // The buffer is transferred into Modbus stack and is not needed here any more
        uart_flush_input(ucUartNumber);
        GEMINAI_LOGD(TAG, "Received data: %u(bytes in buffer)", (unsigned)usCnt);
#if !CONFIG_FMB_TIMER_PORT_ENABLED
        vMBMasterSetCurTimerMode(MB_TMODE_T35);
        xStatus = pxMBMasterPortCBTimerExpired();
        if (!xStatus) {
            xMBMasterPortEventPost(EV_MASTER_FRAME_RECEIVED);
            GEMINAI_LOGD(TAG, "Send additional RX ready event.");
        }
#endif
    } else {
        GEMINAI_LOGE(TAG, "%s: bRxState disabled but junk data (%u bytes) received. ", 
                            __func__, (unsigned)xEventSize);
    }
    return usCnt;
}

BOOL xMBMasterPortSerialTxPoll(void)
{
    USHORT usCount = 0;
    BOOL bNeedPoll = TRUE;

    if( bTxStateEnabled ) {
        // Continue while all response bytes put in buffer or out of buffer
        while(bNeedPoll && (usCount++ < MB_SERIAL_BUF_SIZE)) {
            // Calls the modbus stack callback function to let it fill the UART transmit buffer.
            bNeedPoll = pxMBMasterFrameCBTransmitterEmpty( ); // callback to transmit FSM
        }
        GEMINAI_LOGD(TAG, "MB_TX_buffer sent: (%u) bytes.", (unsigned)(usCount - 1));
        // Waits while UART sending the packet
        esp_err_t xTxStatus = uart_wait_tx_done(ucUartNumber, MB_SERIAL_TX_TOUT_TICKS);
        vMBMasterPortSerialEnable(TRUE, FALSE);
        MB_PORT_CHECK((xTxStatus == ESP_OK), FALSE, "mb serial sent buffer failure.");
        return TRUE;
    }
    return FALSE;
}

// UART receive event task
static void vUartTask(void* pvParameters)
{
    uart_event_t xEvent;
    USHORT usResult = 0;
    for(;;) {
        if (xMBPortSerialWaitEvent(xMbUartQueue, (void*)&xEvent, portMAX_DELAY)) {
            GEMINAI_LOGD(TAG, "MB_uart[%u] event:", (unsigned)ucUartNumber);
            switch(xEvent.type) {
                //Event of UART receiving data
                case UART_DATA:
                    GEMINAI_LOGD(TAG,"Data event, len: %u.", (unsigned)xEvent.size);
                    // This flag set in the event means that no more
                    // data received during configured timeout and UART TOUT feature is triggered
                    if (xEvent.timeout_flag) {
                        // Response is received but previous packet processing is pending
                        // Do not wait completion of processing and just discard received data as incorrect
                        if (vMBMasterRxSemaIsBusy()) {
                            vMBMasterRxFlush();
                            break;
                        }
                        // Get buffered data length
                        ESP_ERROR_CHECK(uart_get_buffered_data_len(ucUartNumber, &xEvent.size));
                        // Read received data and send it to modbus stack
                        usResult = usMBMasterPortSerialRxPoll(xEvent.size);
                        GEMINAI_LOGD(TAG,"Timeout occured, processed: %u bytes", (unsigned)usResult);
                    }
                    break;
                //Event of HW FIFO overflow detected
                case UART_FIFO_OVF:
                    GEMINAI_LOGD(TAG, "hw fifo overflow.");
                    xQueueReset(xMbUartQueue);
                    break;
                //Event of UART ring buffer full
                case UART_BUFFER_FULL:
                    GEMINAI_LOGD(TAG, "ring buffer full.");
                    xQueueReset(xMbUartQueue);
                    uart_flush_input(ucUartNumber);
                    break;
                //Event of UART RX break detected
                case UART_BREAK:
                    GEMINAI_LOGD(TAG, "uart rx break.");
                    break;
                //Event of UART parity check error
                case UART_PARITY_ERR:
                    GEMINAI_LOGD(TAG, "uart parity error.");
                    xQueueReset(xMbUartQueue);
                    uart_flush_input(ucUartNumber);
                    break;
                //Event of UART frame error
                case UART_FRAME_ERR:
                    GEMINAI_LOGD(TAG, "uart frame error.");
                    xQueueReset(xMbUartQueue);
                    uart_flush_input(ucUartNumber);
                    break;
                default:
                    GEMINAI_LOGD(TAG, "uart event type: %u.", (unsigned)xEvent.type);
                    break;
            }
        }
    }
    vTaskDelete(NULL);
}

BOOL xMBMasterPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
    esp_err_t xErr = ESP_OK;
    // Set communication port number
    ucUartNumber = ucPORT;
    // Configure serial communication parameters
    UCHAR ucParity = UART_PARITY_DISABLE;
    UCHAR ucData = UART_DATA_8_BITS;
    switch(eParity){
        case MB_PAR_NONE:
            ucParity = UART_PARITY_DISABLE;
            break;
        case MB_PAR_ODD:
            ucParity = UART_PARITY_ODD;
            break;
        case MB_PAR_EVEN:
            ucParity = UART_PARITY_EVEN;
            break;
        default:
            GEMINAI_LOGE(TAG, "Incorrect parity option: %u", (unsigned)eParity);
            return FALSE;
    }
    switch(ucDataBits){
        case 5:
            ucData = UART_DATA_5_BITS;
            break;
        case 6:
            ucData = UART_DATA_6_BITS;
            break;
        case 7:
            ucData = UART_DATA_7_BITS;
            break;
        case 8:
            ucData = UART_DATA_8_BITS;
            break;
        default:
            ucData = UART_DATA_8_BITS;
            break;
    }
    uart_config_t xUartConfig = {
        .baud_rate = ulBaudRate,
        .data_bits = ucData,
        .parity = ucParity,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
        .rx_flow_ctrl_thresh = 2,
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0))
        .source_clk = UART_SCLK_DEFAULT,
#else
        .source_clk = UART_SCLK_APB,
#endif
    };
    // Set UART config
    xErr = uart_param_config(ucUartNumber, &xUartConfig);
    MB_PORT_CHECK((xErr == ESP_OK),
            FALSE, "mb config failure, uart_param_config() returned (0x%x).", (int)xErr);
    // Install UART driver, and get the queue.
    xErr = uart_driver_install(ucUartNumber, 1024*8, 2048,
                                    MB_QUEUE_LENGTH, &xMbUartQueue, MB_PORT_SERIAL_ISR_FLAG);
    MB_PORT_CHECK((xErr == ESP_OK), FALSE,
            "mb serial driver failure, uart_driver_install() returned (0x%x).", (int)xErr);
    // Set timeout for TOUT interrupt (T3.5 modbus time)
    xErr = uart_set_rx_timeout(ucUartNumber, MB_SERIAL_TOUT);
    MB_PORT_CHECK((xErr == ESP_OK), FALSE,
            "mb serial set rx timeout failure, uart_set_rx_timeout() returned (0x%x).", (int)xErr);

    // Set always timeout flag to trigger timeout interrupt even after rx fifo full
    uart_set_always_rx_timeout(ucUartNumber, true);
    MB_PORT_CHECK((xMBMasterPortRxSemaInit()), FALSE,
                        "mb serial RX semaphore create fail.");
    // Create a task to handle UART events
    BaseType_t xStatus = xTaskCreatePinnedToCore(vUartTask, "uart_queue_task",
                                                    MB_SERIAL_TASK_STACK_SIZE,
                                                    NULL, MB_SERIAL_TASK_PRIO,
                                                    &xMbTaskHandle, MB_PORT_TASK_AFFINITY);
    if (xStatus != pdPASS) {
        vTaskDelete(xMbTaskHandle);
        // Force exit from function with failure
        MB_PORT_CHECK(FALSE, FALSE,
                "mb stack serial task creation error. xTaskCreate() returned (0x%x).", (int)xStatus);
    } else {
        vTaskSuspend(xMbTaskHandle); // Suspend serial task while stack is not started
    }
    GEMINAI_LOGD(MB_PORT_TAG,"%s Init serial.", __func__);
    return TRUE;
}

void vMBMasterPortSerialClose(void)
{
    vMBMasterPortRxSemaClose();
    (void)vTaskDelete(xMbTaskHandle);
    ESP_ERROR_CHECK(uart_driver_delete(ucUartNumber));
}

BOOL xMBMasterPortSerialPutByte(CHAR ucByte)
{
    // Send one byte to UART transmission buffer
    // This function is called by Modbus stack
    UCHAR ucLength = uart_write_bytes(ucUartNumber, &ucByte, 1);
    return (ucLength == 1);
}

// Get one byte from intermediate RX buffer
BOOL xMBMasterPortSerialGetByte(CHAR* pucByte)
{
    assert(pucByte != NULL);
    USHORT usLength = uart_read_bytes(ucUartNumber, (uint8_t*)pucByte, 1, MB_SERIAL_RX_TOUT_TICKS);
    return (usLength == 1);
}

static void IRAM_ATTR vTimerAlarmCBHandler(void *param)
{
    pxMBMasterPortCBTimerExpired(); // Timer expired callback function
    pxTimerContext->xTimerState = TRUE;
    ESP_EARLY_LOGD(TAG, "Timer mode: (%u) triggered", (unsigned)xMBMasterGetCurTimerMode());
}

BOOL xMBMasterPortTimersInit(USHORT usTimeOut50us)
{
    MB_PORT_CHECK((usTimeOut50us > 0), FALSE,
            "Modbus timeout discreet is incorrect.");
    MB_PORT_CHECK(!pxTimerContext, FALSE,
                "Modbus timer is already created.");
    pxTimerContext = calloc(1, sizeof(xTimerContext_t));
    if (!pxTimerContext) {
        return FALSE;
    }
    pxTimerContext->xTimerIntHandle = NULL;
    // Save timer reload value for Modbus T35 period
    pxTimerContext->usT35Ticks = usTimeOut50us;
    esp_timer_create_args_t xTimerConf = {
        .callback = vTimerAlarmCBHandler,
        .arg = NULL,
#if (MB_TIMER_SUPPORTS_ISR_DISPATCH_METHOD && CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD)
        .dispatch_method = ESP_TIMER_ISR,
#else
        .dispatch_method = ESP_TIMER_TASK,
#endif
        .name = "MBM_T35timer"
    };
    // Create Modbus timer
    esp_err_t xErr = esp_timer_create(&xTimerConf, &(pxTimerContext->xTimerIntHandle));
    if (xErr) {
        return FALSE;
    }
    return TRUE;
}

// Set timer alarm value
static BOOL xMBMasterPortTimersEnable(uint64_t xToutUs)
{
    MB_PORT_CHECK(pxTimerContext && (pxTimerContext->xTimerIntHandle), FALSE,
                                "timer is not initialized.");
    MB_PORT_CHECK((xToutUs > 0), FALSE,
                            "incorrect tick value for timer = (0x%llu).", xToutUs);
    esp_timer_stop(pxTimerContext->xTimerIntHandle);
    esp_timer_start_once(pxTimerContext->xTimerIntHandle, xToutUs);
    pxTimerContext->xTimerState = FALSE;
    return TRUE;
}

void vMBMasterPortTimersT35Enable(void)
{
    uint64_t xToutUs = (pxTimerContext->usT35Ticks * MB_TIMER_TICK_TIME_US);

    // Set current timer mode, don't change it.
    vMBMasterSetCurTimerMode(MB_TMODE_T35);
    // Set timer alarm
    (void)xMBMasterPortTimersEnable(xToutUs);
}

void vMBMasterPortTimersConvertDelayEnable(void)
{
    // Covert time in milliseconds into ticks
    uint64_t xToutUs = (MB_MASTER_DELAY_MS_CONVERT * 1000);

    // Set current timer mode
    vMBMasterSetCurTimerMode(MB_TMODE_CONVERT_DELAY);
    GEMINAI_LOGD(MB_PORT_TAG,"%s Convert delay enable.", __func__);
    (void)xMBMasterPortTimersEnable(xToutUs);
}

void vMBMasterPortTimersRespondTimeoutEnable(void)
{
    uint64_t xToutUs = (MB_MASTER_TIMEOUT_MS_RESPOND * 1000);

    vMBMasterSetCurTimerMode(MB_TMODE_RESPOND_TIMEOUT);
    GEMINAI_LOGD(MB_PORT_TAG,"%s Respond enable timeout.", __func__);
    (void)xMBMasterPortTimersEnable(xToutUs);
}

void MB_PORT_ISR_ATTR
vMBMasterPortTimersDisable()
{
    // Disable timer alarm
    esp_timer_stop(pxTimerContext->xTimerIntHandle);
}

void vMBMasterPortTimerClose(void)
{
    // Delete active timer
    if (pxTimerContext) {
        if (pxTimerContext->xTimerIntHandle) {
            esp_timer_stop(pxTimerContext->xTimerIntHandle);
            esp_timer_delete(pxTimerContext->xTimerIntHandle);
        }
        free(pxTimerContext);
        pxTimerContext = NULL;
    }
}
